/******************************************************************************
 cnome: A C++ source "genes" discover. :-)

 See more details at:

   http://www.codeplex.com/cnome

 The original author site is at: http://www.alemosbarbosa.com/

 Copyright (c) 2008-2010 Alexandre Lemos Barbosa.
 Distributed under the MIT License. (See accompanying file LICENSE-MIT.txt 
 or at http://www.codeplex.com/cnome/license)
 ******************************************************************************/

#include "cnome_config.h"

#include <boost/spirit/include/classic_core.hpp>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>

#include "auxiliar.h"
#include "cnome.h"

namespace cnome {
cpp const cnome_p;
}

using namespace boost::spirit::classic;
using namespace boost::wave;
using namespace cnome;
using namespace std;

void cnome_invalid_parameter_handler(const wchar_t * a, const wchar_t * b, const wchar_t * c, unsigned int d, uintptr_t e)
{
    //throw exception("cnome_invalid_parameter_handler");
    throw exception();  // TODO (alb): Implement a better way to handle this situation. Define a new type of exception
}                       // 08dec2010

///////////////////////////////////////////////////////////////////////////////
// main entry point
int main(int argc, char *argv[]) {
    if (2 != argc) {
        std::cerr << "Usage: cnome <cpp file>" << std::endl;
        return -1;
    }
#ifdef _WIN    // TODO: Find the appropriate macro name
    _set_invalid_parameter_handler(cnome_invalid_parameter_handler);
    // TODO: Implement in other platforms
#endif

    // current file position is saved for exception handling
    boost::wave::util::file_position_type current_position;

    try {
        //  Open and read in the specified input file.
        std::ifstream instream(argv[1]);
        std::string instring;

        if (!instream.is_open()) {
            std::cerr << "Could not open input file: " << argv[1] << std::endl;
            return -2;
        }
        instream.unsetf(std::ios::skipws);
        instring = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
            std::istreambuf_iterator<char>());

        // The preprocessor iterator shouldn't be constructed directly. It is 
        // to be generated through a wave::context<> object. This wave:context<> 
        // object is to be used additionally to initialize and define different 
        // parameters of the actual preprocessing (not done here).
        //
        // The preprocessing of the input stream is done on the fly behind the 
        // scenes during iteration over the context_t::iterator stream.
        context_t ctx (instring.begin(), instring.end(), argv[1]);
        readConfig(ctx);
        // analyze the input file
		cnome::iterator first = ctx.begin();
		cnome::iterator last = ctx.end();
		parse_info<cnome::iterator> info = parse(first, last, cnome_p);

        if (info.full)
        {
            cout << "-------------------------\n";
            cout << "Parsing succeeded\n";
            cout << "-------------------------\n";
        }
        else
        {
            cout << "-------------------------\n";
            cout << "Parsing failed\n";
            cout << "stopped at: " << info.stop->get_position().get_file() << ":" 
                 << info.stop->get_position().get_line() << "," << info.stop->get_position().get_column() 
                 << " : \" " 
                 << info.stop->get_value() << "\"\n";

            // TODO: Find out how to identify that info.stop.getFarest has a valid position
            const cnome::iterator& farest = info.stop.getFarest();
            cnome::token_t farestTk;
            if (farest != info.stop.end) {
                farestTk = *info.stop.getFarest();
            }
            if (farest == info.stop.end) {
                cout << "farest reached: " << "end of file" ;
            }
            else if (info.stop.getFarest()->is_valid()) {
                cout << "farest reached: " << info.stop.getFarest()->get_position().get_file() << ":" ;
                cout << info.stop.getFarest()->get_position().get_line() << ",";
                cout << info.stop.getFarest()->get_position().get_column() << ",";
                cout << info.stop.getFarest().get_cnome_position();
                cout << " : \" " ;
                cout << info.stop.getFarest()->get_value() << "\"\n";
                cout << "-------------------------\n";
            }
            else {
                cout << "farest reached: " << "unknown" ;
            }
        }
        PrintSymbols();
    }
    catch (cnome_parser_error& e)
    {
        // some preprocessing error
        std::cerr 
            << e.where->get_position().get_file() << "(" 
            << e.where->get_position().get_line() << ','
            << e.where->get_position().get_column() <<"): "
            << e.descriptor << std::endl;
        
        PrintSymbols();
        return 2;
    }
    catch (boost::wave::cpp_exception &e) 
    {
        // some preprocessing error
        std::cerr 
            << e.file_name() << "(" << e.line_no() << "," << e.column_no() << "): "
            << e.description() << std::endl;
        PrintSymbols();
        return 2;
    }
	catch (boost::spirit::classic::parser_error<std::string, cnome::iterator> &e)
    {
        // use last recognized token to retrieve the error position
        // some preprocessing error
        std::cerr 
            << e.where->get_position().get_file() << "(" 
            << e.where->get_position().get_line() << ','
            << e.where->get_position().get_column() <<"): "
            << e.descriptor << std::endl;
        
        PrintSymbols();
        return 3;
    }
    catch (std::exception &e) {
        // use last recognized token to retrieve the error position
        std::cerr 
            << current_position.get_file() 
            << "(" << current_position.get_line() << "): "
            << "exception caught: " << e.what()
            << std::endl;
        PrintSymbols();
        return 3;
    }
    catch (...) {
        // use last recognized token to retrieve the error position
        std::cerr 
            << current_position.get_file() 
            << "(" << current_position.get_line() << "): "
            << "unexpected exception caught." << std::endl;
        PrintSymbols();
        return 4;
    }
    return 0;
}
